package org.hawk.gwt.ppc.javacc;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.hawk.gwt.ppc.scanner.Processor;

/**
 * Facade for the javacc generated code to decrease coupling between 
 * different parts of code.
 * @author alex.bereznevatiy@gmail.com
 *
 */
public class JavaScanner {
	protected final Map<Integer,List<Processor>> processors;
	/**
	 * Creates a scanner.
	 * Writes content to output stream.
	 * @param input
	 * @param output 
	 */
	public JavaScanner(){
		processors = new HashMap<Integer, List<Processor>>();
	}
	/**
	 * Starts scanning process for the input stream specified.
	 * @param input
	 * @param output 
	 * @throws ParseException
	 */
	public void startScan(InputStream input, OutputStream output) throws ParseException {
		startScan(new InputStreamReader(input), output==null ? null : new OutputStreamWriter(output));
	}
	/**
	 * Starts scanning process for the input reader specified.
	 * @param input
	 * @param output 
	 * @throws ParseException
	 */
	public void startScan(Reader input, Writer output) throws ParseException {
		JavaParser parser = new JavaParser(input, output, this);
		parser.CompilationUnit();
	}
	/**
	 * Adds processor to the current scanner. Processor will be called 
	 * for node specified.
	 * @param processor
	 */
	public void addProcessor(int nodeType, Processor processor) {
		if(processors.get(nodeType)==null){
			processors.put(nodeType, new LinkedList<Processor>());
		}
		processors.get(nodeType).add(processor);
	}
	
	void openNode(SimpleNode node, TokenChainIterator iterator) throws ParseException {
		try {
			for(Processor p : getPreprocessorsForKind(node.id)){
				p.visitStart(iterator,node.id);
			}
		}
		catch(IOException ex){
			throw new ParseException(ex);
		}
	}
	
	protected List<Processor> getPreprocessorsForKind(int kind) {
		if(processors.get(kind)==null){
			return Collections.emptyList();
		}
		return Collections.unmodifiableList(processors.get(kind));
	}
	
	void closeNode(SimpleNode node, TokenChainIterator iterator) throws ParseException {
		try {
			int index = iterator.index();
			for(Processor p : getPreprocessorsForKind(node.id)){
				iterator.moveTo(index);
				p.visitEnd(iterator,node.id);
			}
		}
		catch(IOException ex){
			throw new ParseException(ex);
		}
	}
}
